﻿// <copyright file="AbstractOperation.cs" company="EDD Solutions Ltd.">
// Copyright (c) EDD Solutions Ltd. 2010.
//
// This file is part of Sharepoint.Manipulator.Net.
//
// Sharepoint.Manipulator.Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Sharepoint.Manipulator.Net is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Sharepoint.Manipulator.Net.  If not, see http://www.gnu.org/licenses/.
// </copyright>
// <author>$Author$</author>
// <date>$Date$</date>
// <revision>$Revision$</revision>
namespace Sharepoint.Manipulator.Net
{
    using System;
    using System.Text.RegularExpressions;

    /// <summary>
    /// Abstract implementation of IOperation. Implementing common functionality.
    /// </summary>
    public abstract class AbstractOperation : IOperation
    {
        /// <summary>
        /// The regular expression pattern used to read a URL
        /// </summary>
        private const string UrlRegexPattern = @"^(?<s1>(?<s0>[^:/\?#]+):)?(?<a1>"
            + @"//(?<a0>[^/\?#]*))?(?<p0>[^\?#]*)"
            + @"(?<q1>\?(?<q0>[^#]*))?"
            + @"(?<f1>#(?<f0>.*))?";

        /// <summary>
        /// The root url of the sharepoint site.
        /// </summary>
        private string rootUrl;

        /// <summary>
        /// The id of the sharepoint site
        /// </summary>
        private string siteId;

        /// <summary>
        /// The full url of the sharepoint site.
        /// </summary>
        private string fullSiteUrl;

        /// <summary>
        /// The type of the sharepoint lits/calendar etc
        /// </summary>
        private string entityType;

        /// <summary>
        /// The id of the sharepoint lits/calendar etc
        /// </summary>
        private string entityId;

        /// <summary>
        /// The object to use to communictae with the sharepoint entity.
        /// </summary>
        private IEntity entity;

        /// <summary>
        /// The id of the sharepoint item.
        /// </summary>
        private string itemId;

        /// <summary>
        /// The responcse message after running the operation.
        /// </summary>
        private string runResponse;

        /// <summary>
        /// The regular expression used to read a url
        /// </summary>
        private Regex urlRegex = new Regex(UrlRegexPattern, RegexOptions.ExplicitCapture);

        /// <summary>
        /// Gets or sets the run response message.
        /// </summary>
        public string RunResponse
        {
            get { return this.runResponse; }
            set { this.runResponse = value; }
        }

        /// <summary>
        /// Gets or sets the root url of the sharepoint site.
        /// </summary>
        public string RootUrl
        {
            get { return this.rootUrl; }
            set { this.rootUrl = value; }
        }

        /// <summary>
        /// Gets or sets the id of the sharepoint site
        /// </summary>
        public string SiteId
        {
            get { return this.siteId; }
            set { this.siteId = value; }
        }

        /// <summary>
        /// Gets or sets the full url of the sharepoint site
        /// </summary>
        public string FullSiteUrl
        {
            get { return this.fullSiteUrl; }
            set { this.fullSiteUrl = value; }
        }

        /// <summary>
        /// Gets or sets the type of the sharepoint lits/calendar etc
        /// </summary>
        public string EntityType
        {
            get { return this.entityType; }
            set { this.entityType = value; }
        }

        /// <summary>
        /// Gets or sets the id of the sharepoint lits/calendar etc
        /// </summary>
        public string EntityId
        {
            get { return this.entityId; }
            set { this.entityId = value; }
        }

        /// <summary>
        /// Gets or sets the entity.
        /// </summary>
        public IEntity Entity
        {
            get { return this.entity; }
            set { this.entity = value; }
        }

        /// <summary>
        /// Gets or sets the id of the sharepoint item.
        /// </summary>
        public string ItemId
        {
            get { return this.itemId; }
            set { this.itemId = value; }
        }

        #region IOperation Members

        /// <summary>
        /// Run the operation.
        /// </summary>
        /// <returns>an object with the result of the run</returns>
        public abstract object Run();

        /// <summary>
        /// Retturn the usage instructions for the operation.
        /// </summary>
        /// <returns>a string of instructions.</returns>
        public abstract string Usage();

        /// <summary>
        /// Parse the incoming parameters.
        /// </summary>
        /// <param name="args">the parameters</param>
        public abstract void ParseParameters(string[] args);

        /// <summary>
        /// Confirm that the operation can be carried out of the location
        /// </summary>
        public abstract void ValidateLocation();

        #endregion

        /// <summary>
        /// Interpret the location either as a URL or as a coded location.
        /// </summary>
        /// <param name="location">the location string</param>
        protected void ParseLocation(string location)
        {
            if (location.StartsWith("http"))
            {
                this.ParseUrl(location);
            }
            else
            {
                this.ParseCodedLocation(location);
            }

            this.fullSiteUrl = this.rootUrl + "/" + this.siteId + "/";
        }

        /// <summary>
        /// Parse a location defined as a URL
        /// </summary>
        /// <param name="location">the location</param>
        private void ParseCodedLocation(string location)
        {
            string[] parts = location.Split(new char[] { ';' });
            if (parts.Length == 0)
            {
                throw new Exception(string.Format("Unable to interpret location [{0}]", new string[] { location }));
            }

            int tempIndex;

            // the last entry
            tempIndex = parts.Length - 1;
            if (tempIndex < 0)
            {
                return;
            }

            this.ItemId = parts[tempIndex].ToLower();

            tempIndex = parts.Length - 2;
            if (tempIndex < 0)
            {
                return;
            }

            this.EntityId = parts[tempIndex].ToLower();

            tempIndex = parts.Length - 3;
            if (tempIndex < 0)
            {
                return;
            }

            this.EntityType = parts[tempIndex].ToLower();

            tempIndex = parts.Length - 4;
            if (tempIndex < 0)
            {
                return;
            }

            this.SiteId = parts[tempIndex].ToLower();

            tempIndex = parts.Length - 5;
            if (tempIndex < 0)
            {
                return;
            }

            this.RootUrl = "http://" + parts[tempIndex].ToLower() + "/";
        }

        /// <summary>
        /// Parse a location defined as a URL
        /// </summary>
        /// <param name="location">the location</param>
        private void ParseUrl(string location)
        {
            // is a url
            Match m = this.urlRegex.Match(location);

            this.rootUrl = m.Groups["s1"].Value.ToLower() + m.Groups["a1"].Value.ToLower(); // (Scheme with colon) + (Authority with //)

            string path = m.Groups["p0"].Value.ToLower(); // (Path);
            string[] pathParts = path.Split(new char[] { '/' });
            this.entityId = pathParts[pathParts.Length - 2];
            this.entityType = pathParts[pathParts.Length - 3];
            if (pathParts.Length == 5)
            {
                this.siteId = pathParts[pathParts.Length - 4];
            }

            string query = m.Groups["q0"].Value.ToLower(); // (Query without ?);
            string[] nameEqValuePairs = query.Split(new char[] { '&' });
            foreach (string nameEqValuePair in nameEqValuePairs)
            {
                string[] nameAndValue = nameEqValuePair.Split(new char[] { '=' });
                if (nameAndValue[0].ToLower().Equals("id"))
                {
                    this.itemId = nameAndValue[1];
                }
            }
        }
    }
}
